東方老桜夢の技術解説 枠付きスクロールについて

東方老桜夢では、弾幕密度を上げるためにゲーム画面を半分サイズにし、 左側にゲーム画面、右側にスコアなどの表示を行っています。

ファミコンはＢＧ面を１つしか持たないため、ハードウェアの機能では 枠が付いた状態で１ドット単位で背景がスクロールする画面を 作ることはできません。
ソフトウェアで工夫することで実現しています。

仕組み

基本的な仕組みは、ネームテーブル書き換えのソフトウェアスクロール と CHRバンク切り替えによる１ドットスクロール での補間です。

ネームテーブル書き換えのソフトウェアスクロールとは、 MSX1などのレトロＰＣでよくみる画面書き換えによる８ドット単位のスクロールです。
東方老桜夢では、スクロールする背景部分のネームテーブルを全面書き換えています。

ファミコンでは、１フレーム辺りのVRAMへのデータ転送量は十分にはありません。
そこで、画面を２つ用意し、片方(表面)を表示している間、もう片方(裏面)を数フレームかけて書き換え、 表面と裏面を入れ替える方法を採用しています。
いわゆるダブルバッファ法です。

CHRバンク切り替えによる１ドットスクロールとは、 １ドットずつずれたキャラクタパターンを８つのCHRバンクに格納し、
CHRバンクを順番に切り替えることで、見かけ上１ドット単位で スクロールしているように見せる手法です。
画面がスクロールするアニメーションを見せていると言ってもよいでしょう、

スクロールする方向は、生成するキャラクタパターンのずれの方向で決まります。
必要であれば、同じ画面内で異なる方向へスクロールすることも可能です。
(５面道中や、妖夢、藍のスペカ背景など)

bgscroll1.png

(1～8 はCHRバンクを順番に切り替えて１ドットずつずれた絵を出す。
9で表面と裏面を切り替えてネームテーブルを書き換えた絵を出す、CHRバンクも最初のバンクに戻す。)

レイヤー

スクロールする背景部分は、仮想的に４階層のレイヤーを持たせています。
各レイヤーは表示/非表示を設定でき、表示状態で最も優先度が高いレイヤーが VRAMに転送する画面となります。

レイヤーの内訳は次の通りです。

優先度VRAM転送元データの場所用途レイヤー３高WRAM自機ボム表示レイヤー２WRAM敵スペカの弾丸表示などレイヤー１PRG-ROM敵スペカの背景レイヤー０低PRG-ROM通常の背景

レイヤー０とレイヤー１は、自機や敵キャラの位置に依存しない背景であるため、 決められた背景データをPRG-ROMに配置し、シーケンスデータに従って VRAMに転送します。
一方、レイヤー２とレイヤー３は、自機や敵キャラの依存する背景のため、 WRAMに設けられた仮想VRAMに動的にデータを作成して、 そのデータをVRAMに転送します。

ネームテーブルの書き換えの詳細

スクロールする背景部分のネームテーブルの書き換えは、 ８フレームを１単位として処理します。
これは、１フレームで１ドットスクロールするとして、 ８フレームで１キャラ分(８ドット)ずれて表面と裏面が入れ替わることを 想定したためです。

スクロールする背景部分のネームテーブルの書き換えの処理は、 PRG-ROMのデータを転送する場合とWRAMの仮想VRAMから転送する場合で 違いがあります。

PRG-ROMからの転送

１単位８フレーム中の各処理は次の通りです。

フレーム 処理

０フレーム目 転送なし

１フレーム目 画面の1/6を転送(16キャラ×４ライン)

２フレーム目 画面の1/6を転送(16キャラ×４ライン)

３フレーム目 画面の1/6を転送(16キャラ×４ライン)

４フレーム目 画面の1/6を転送(16キャラ×４ライン)

５フレーム目 画面の1/6を転送(16キャラ×４ライン)

６フレーム目 画面の1/6を転送(16キャラ×４ライン)

７フレーム目 アトリビュート領域を転送、表面と裏面の入れ替え

１～６フレーム目では、１フレーム辺りのVRAMへの最大転送量の半分を使って 裏面のスクロールする背景部分を書き換えます。
VRAMへの最大転送量の残り半分は、スコアなどの書き換えに使用します。

７フレーム目では、アトリビュート領域(ＢＧのパレット指定データ)と 表面と裏面の入れ替えを同フレームで行っています。
これは、とある理由でアトリビュート領域は表面裏面で共通となっているため、 表面と裏面を入れ替えるタイミングでアトリビュート領域を更新しなければ ならないためです。

１フレームで１ドットずれるスクロールのスピードは演出的には速く見えるため、 実際には３２フレームや６４フレームなどの２のｎ乗倍遅いスピードで 使用しています。
この時、ネームテーブル書き換えの処理は、表面と裏面を入れ替える ８フレーム前から開始して、それより前のフレームでは転送処理を休止しています。

WRAMからの転送

１単位８フレーム中の各処理は次の通りです。

フレーム 処理

０フレーム目 画面の1/3を転送(16キャラ×８ライン)

１フレーム目 画面の1/3を転送(16キャラ×８ライン)

２フレーム目 画面の1/3を転送(16キャラ×８ライン)

３フレーム目 アトリビュート領域の転送
(表示するレイヤー切り替え後、１回のみ)
表面と裏面の入れ替え

４フレーム目 画面の1/3を転送(16キャラ×８ライン)

５フレーム目 画面の1/3を転送(16キャラ×８ライン)
６フレーム目 画面の1/3を転送(16キャラ×８ライン)

７フレーム目 アトリビュート領域の転送
(表示するレイヤー切り替え後、１回のみ)
表面と裏面の入れ替え

WRAMからの転送では、仮想VRAMに動的に作成したデータを いち早く画面に反映させるため、４フレームで画面を書き換え、 ８フレーム中２回表面と裏面を入れ替えています。

０～２、４～６フレーム目では、１フレーム辺りのVRAMへの最大転送量を全て使って 裏面のスクロールする背景部分を書き換えます。

３、７フレーム目では、表示するレイヤーを切り替えた後１回だけ アトリビュート領域を転送します。
アトリビュート領域の転送を行った以降のフレームでは、 スコアなどの書き換えを行います。

WRAMの仮想VRAMは、処理落ちが発生していなければ、更新されているため、 VRAMへの転送処理も常時稼働する動きとなります。